using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Threading;
using Risiko.State;


namespace Risiko.Logic
{
  public enum HasNextMove
  {
    Thinking,
    Yes,
    No
  }

  public struct Attack
  {
    public bool isValid;
    public State.TerritoryReference from, to;
    public int numArmies;
  }

  public struct ArmiesPlacement
  {
    public bool isValid;
    public State.Territory territory;
    public int numArmies;
  }

  public struct MoveArmies
  {
    public bool isValid;
    public State.TerritoryReference from, to;
    public int numArmies;
  }

  public struct CardMove
  {
    public bool isValid;
    public State.Card card1;
    public State.Card card2;
    public State.Card card3;
  }

  public abstract class PlayerBehavior : Microsoft.Xna.Framework.GameComponent
  {
    protected List<PlayerBehavior> otherPlayers;
    public State.PlayerList selfID;
    //public State.PlayerState selfState;
    public State.State gameState;

    public HasNextMove hasNextMove;
    public HasNextMove hasCardMove;
    public HasNextMove hasNextAttack;
    public HasNextMove hasStrategicMove;
    public HasNextMove hasConquerMove;

    public PlayerBehavior(Game game)
      : base(game)
    {}

    public override void Initialize()
    {
      gameState = Game.Services.GetService(typeof(State.State)) as State.State;
      //selfState = gameState.playerStates[(int)selfID];
      gameState.playerStates[(int)selfID].CurrArmiesPlacements = new List<ArmiesPlacement>();
      gameState.playerStates[(int)selfID].CurrCardMove = new CardMove() { isValid = false };
      gameState.playerStates[(int)selfID].CurrAttack = new Attack() { isValid = false };
      gameState.playerStates[(int)selfID].CurrMove = new MoveArmies() { isValid = false };
      gameState.playerStates[(int)selfID].tappedTerritories = new List<TerritoryReference>(2);

      // Initialize animation parameters
      gameState.playerStates[(int)selfID].MossaFatta = false;
      gameState.playerStates[(int)selfID].MossaAnimata = true;

      base.Initialize();
    }

    protected Thread Async(Action a)
    {
      var t = new Thread(() => a());
      t.Start();
      return t;
    }

    public override void Update(GameTime gameTime)
    {
      base.Update(gameTime);
    }

    public virtual void OnCurrentAttackWon() {}

    public virtual void OnCurrentAttackLost() {}

    public bool WinCondition2()
    {
      int points = 0;
      gameState.board.IterTerritories(t => points += t.neighbors.Count());

      int myPoints = 0;
      gameState.board.IterTerritories(t => { if (t.owner == selfID) myPoints += t.neighbors.Count(); });

      if (myPoints >= (points / 2) + 1) 
        return true;

      return false;
    }

    public bool HasWorld()
    {     
      for(int i = 0; i < gameState.board.continents.Length; i++)
      {
        if(!HasContinent(gameState.board.continents[i])) return false;  
      }

      return true;
    }

    public bool HasContinent(State.Continent c)
    {
      if (c.territories.All(t => t.owner == selfID)) return true;
      return false;
    }

    public bool HasTerritory(State.Territory t)
    {
      if (gameState.board.continents[t.continentID].territories[t.territoryID].owner == selfID) return true;
      return false;
    }

    public void UpdateTerritory()
    {
      gameState.playerStates[(int)selfID].OwnedTerritories.Clear();

      gameState.board.IterTerritories(t =>
      {
        if (t.owner == selfID)
          gameState.playerStates[(int)selfID].OwnedTerritories.Add(new State.TerritoryReference() { continentID = t.continentID, territoryID = t.territoryID });
      });
    }

    // Apply Moves

    // Placements 
    protected void MakePlacement(ArmiesPlacement placement)
    {
      // Add Armies
      gameState.board.continents[placement.territory.continentID].territories[placement.territory.territoryID].armies +=
      placement.numArmies;

      gameState.playerStates[(int)selfID].reinforcementsNum -= placement.numArmies;
    }

    public void AutoPlaceReinforcements()
    {
      gameState.playerStates[(int)selfID].CurrArmiesPlacements.Clear();
      int numTerr = gameState.playerStates[(int)selfID].OwnedTerritories.Count;

      for(int i = 0; i < gameState.playerStates[(int)selfID].reinforcementsNum; i++)
      {
        // Metto un armata per ogni territorio posseduto
        var currT = i % numTerr;
        gameState.playerStates[(int)selfID].CurrArmiesPlacements.Add(new ArmiesPlacement()
          {
            numArmies = 1,
            territory = gameState.board.GetTerritory(gameState.playerStates[(int)selfID].OwnedTerritories.ElementAt(currT)),
            isValid = true
          });
      }

      //gameState.board.IterTerritories(t =>
      //{
      //  if (t.owner == selfID)
      //  {
      //    if (gameState.playerStates[(int)selfID].reinforcementsNum > 0)
      //    {
      //      var placement = new ArmiesPlacement() { territory = t, numArmies = 1 };
      //      gameState.playerStates[(int)selfID].CurrArmiesPlacements.Add(placement);
      //      gameState.playerStates[(int)selfID].reinforcementsNum--;
      //    }
      //  }
      //});

      for (int i = 0; i < gameState.playerStates[(int)selfID].CurrArmiesPlacements.Count; i++)
      {
        MakePlacement(gameState.playerStates[(int)selfID].CurrArmiesPlacements[i]);
      }

    }

    // Attack

    protected bool conquered = false;
    protected void ApplyAttack()
    {
      Attack attack = gameState.playerStates[(int)selfID].CurrAttack;
      State.Territory t1 = gameState.board.GetTerritory(attack.from);
      State.Territory t2 = gameState.board.GetTerritory(attack.to);
      int numAttacks = Math.Min(attack.numArmies, t2.armies);
      int victory = 0;

      // Tira dadi
      Random rnd = new Random();

      // R = rosso(attacco) - B = Blu(difesa)
      int[] valueR = new int[3];
      int[] valueB = new int[3];

      valueR[0] = rnd.Next(1, 7);
      valueR[1] = rnd.Next(1, 7);
      valueR[2] = rnd.Next(1, 7);
      valueB[0] = rnd.Next(1, 7);
      valueB[1] = rnd.Next(1, 7);
      valueB[2] = rnd.Next(1, 7);

      if (attack.numArmies <= 2) { valueR[1] = 0; valueB[1] = 0; }
      if (attack.numArmies == 1) { valueR[2] = 0; valueB[2] = 0; }

      if (t2.armies <= 2) valueB[1] = 0;
      if (t2.armies == 1) valueB[2] = 0;

      string mess = "Risultato: ";

      if (t1.armies > 1 && t2.armies > 0)
      {
        for (int j = 0; j < numAttacks; j++)
        {
          int R = valueR.Max();
          int B = valueB.Max();

          //Clean del valore massimo
          for (int i = 0; i < 3; i++)
          {
            if (valueR[i] == R) valueR[i] = 0;
            if (valueB[i] == B) valueB[i] = 0;
          }

          mess += "|" + R + "-" + B;

          if (R > B)
          {
            victory++;

            if (t2.armies > 0)
              t2.armies--;

            // Start animations
            gameState.playerStates[(int)selfID].MossaFatta = true;
            gameState.board.ActiveAnimations.Add(new State.Animation() { Type = AnimationType.Explosion, Position = t2.position, TotalTime = 1.0, TRef = attack.to, HFrames = 5, VFrames = 5 });
          }
          else
          {
            if (t1.armies > 0)
              t1.armies--;

            // Start animations
            gameState.playerStates[(int)selfID].MossaFatta = true;
            gameState.board.ActiveAnimations.Add(new State.Animation() { Type = AnimationType.Explosion, Position = t1.position, TotalTime = 1.0, TRef = attack.from, HFrames = 5, VFrames = 5 });
          }
        }

        // Wait for animations
        while (gameState.playerStates[(int)selfID].MossaFatta) { Thread.Sleep(10); }
        while (!gameState.playerStates[(int)selfID].MossaAnimata) { Thread.Sleep(10); }

        mess += "|. ";
      }

      if (t2.armies <= 0)
      {
        // Sposta le armate coinvolte nell'attacco nel territorio conquistato
        int secondPlayer = (int)t2.owner;
        t2.armies = attack.numArmies;
        t1.armies -= attack.numArmies;
        t2.owner = t1.owner;
        conquered = true;

        mess += " Conquista " + t2.name;

        // Aggiorna gli owner dei territori
        gameState.playerStates[(int)selfID].OwnedTerritories.Add(attack.to);
        gameState.playerStates[secondPlayer].OwnedTerritories.Remove(attack.to);

        //players[currentPlayer].UpdateTerritory();
        //players[secondPlayer].UpdateTerritory();

        //winCard = true;

        // Notifica il supervisor della conquista
        gameState.playerStates[(int)selfID].HasConquered = true;
      }

      gameState.Log.Add(gameState.Round + ": " + t1.name + " (" + gameState.playerStates[(int)t1.owner].name + ") attacca " + t2.name + " (" + gameState.playerStates[(int)t2.owner].name + ") con " + attack.numArmies + " armate. " + mess);

      gameState.board.continents[t2.continentID].territories[t2.territoryID] = t2;
      gameState.board.continents[t1.continentID].territories[t1.territoryID] = t1;

      //if (conquered)
      //{
      //  players[currentPlayer].OnCurrentAttackWon();
      //}
      //else
      //{
      //  players[currentPlayer].OnCurrentAttackLost();
      //}
    }

    // Conquer or Strategic Move

    public void MakeMove(MoveArmies m)
    {
      TerritoryReference t1 = m.to;
      TerritoryReference t2 = m.from;
      int Armies = m.numArmies;

      gameState.board.continents[t1.continentID].territories[t1.territoryID].armies -= Armies;
      gameState.board.continents[t2.continentID].territories[t2.territoryID].armies += Armies;
    }



    // CARDS
    protected CardMove ComputeCardCombinations()
    {
      hasCardMove = Logic.HasNextMove.Thinking;

      var emptyCard = new State.Card() { isSelected = false };
      CardMove computedMove;
      computedMove = new CardMove() { isValid = false, card1 = emptyCard , card2 = emptyCard, card3 = emptyCard };

      if (gameState.playerStates[(int)selfID].Cards.Count > 3)
      {
        List<State.Card> typeCard_Artillery = new List<State.Card>();
        List<State.Card> typeCard_Cavalry = new List<State.Card>();
        List<State.Card> typeCard_Infantry = new List<State.Card>();
        List<State.Card> typeCard_Jolly = new List<State.Card>();

        for (int i = 0; i < gameState.playerStates[(int)selfID].Cards.Count; i++)
        {
          State.Card c = gameState.playerStates[(int)selfID].Cards[i];
          if (c.type == State.CardType.Artillery) typeCard_Artillery.Add(c);
          if (c.type == State.CardType.Cavalry) typeCard_Cavalry.Add(c);
          if (c.type == State.CardType.Infantry) typeCard_Infantry.Add(c);
          if (c.type == State.CardType.Jolly) typeCard_Jolly.Add(c);
        }

        // jolly + 2 cannoni o 2 fanti o 2 cavalieri = 12 armate
        if (typeCard_Jolly.Count >= 1)
        {
          if (typeCard_Artillery.Count >= 2)
          {
            computedMove.isValid = true;
            computedMove.card1 = typeCard_Jolly.First();
            computedMove.card2 = typeCard_Artillery[0];
            computedMove.card3 = typeCard_Artillery[1];
            //hasCardMove = Logic.HasNextMove.Yes;
            return computedMove;
          }
          if (typeCard_Artillery.Count >= 2)
          {
            computedMove.isValid = true;
            computedMove.card1 = typeCard_Jolly.First();
            computedMove.card2 = typeCard_Infantry[0];
            computedMove.card3 = typeCard_Infantry[1];
            //hasCardMove = Logic.HasNextMove.Yes;
            return computedMove;
          }
          if (typeCard_Cavalry.Count >= 2)
          {
            computedMove.isValid = true;
            computedMove.card1 = typeCard_Jolly.First();
            computedMove.card2 = typeCard_Cavalry[0];
            computedMove.card3 = typeCard_Cavalry[1];
            //hasCardMove = Logic.HasNextMove.Yes;
            return computedMove;
          }
        }

        // un cannone, un fante e un cavaliere = 10 armate
        if (typeCard_Artillery.Count >= 1 && typeCard_Infantry.Count >= 1 && typeCard_Cavalry.Count >= 1)
        {
          computedMove.isValid = true;
          computedMove.card1 = typeCard_Artillery.First();
          computedMove.card2 = typeCard_Infantry.First();
          computedMove.card3 = typeCard_Cavalry.First();
          //hasCardMove = Logic.HasNextMove.Yes;
          return computedMove;
        }

        // tre cavalieri = 8 armate
        if (typeCard_Cavalry.Count >= 3)
        {
          computedMove.isValid = true;
          computedMove.card1 = typeCard_Cavalry[0];
          computedMove.card2 = typeCard_Cavalry[1];
          computedMove.card3 = typeCard_Cavalry[2];
          //hasCardMove = Logic.HasNextMove.Yes;
          return computedMove;
        }

        // tre fanti = 6 armate
        if (typeCard_Infantry.Count >= 3)
        {
          computedMove.isValid = true;
          computedMove.card1 = typeCard_Infantry[0];
          computedMove.card2 = typeCard_Infantry[1];
          computedMove.card3 = typeCard_Infantry[2];
          //hasCardMove = Logic.HasNextMove.Yes;
          return computedMove;
        }

        // tre cannoni = 4 armate
        if (typeCard_Artillery.Count >= 3)
        {
          computedMove.isValid = true;
          computedMove.card1 = typeCard_Artillery[0];
          computedMove.card2 = typeCard_Artillery[1];
          computedMove.card3 = typeCard_Artillery[2];
          //hasCardMove = Logic.HasNextMove.Yes;
          return computedMove;
        }

      }
      hasCardMove = Logic.HasNextMove.No;
      return computedMove;
    }

    //public void HasAttackChoise()
    //  {
    //    gameState.playerStates[(int)selfID].TerritoryRef[1]

    //  }

    public int PlayCards(CardMove cm)
    { 
      int reinforcements = 0;

      // tre cannoni = 4 armate
      if (cm.card1.type == State.CardType.Artillery && cm.card2.type == State.CardType.Artillery && cm.card3.type == State.CardType.Artillery) 
        reinforcements = (int)State.CardCombination.ThreeArtillery;

      // tre fanti = 6 armate
      if (cm.card1.type == State.CardType.Infantry && cm.card2.type == State.CardType.Infantry && cm.card3.type == State.CardType.Infantry) 
        reinforcements = (int)State.CardCombination.ThreeInfantry;

      // tre cavalieri = 8 armate
      if (cm.card1.type == State.CardType.Cavalry && cm.card2.type == State.CardType.Cavalry && cm.card3.type == State.CardType.Cavalry) 
        reinforcements = (int)State.CardCombination.ThreeCavalry;

      // un cannone, un fante e un cavaliere = 10 armate
      if (cm.card1.type == State.CardType.Artillery && cm.card2.type == State.CardType.Infantry && cm.card3.type == State.CardType.Cavalry)
        reinforcements = (int)State.CardCombination.Different;

      // jolly + 2 cannoni o 2 fanti o 2 cavalieri = 12 armate
      if (cm.card1.type == State.CardType.Jolly)
        reinforcements = (int)State.CardCombination.JollyCombination;

      //if (cm.card1.territory.owner == selfID) reinforcements += 2;
      //if (cm.card2.territory.owner == selfID) reinforcements += 2;
      //if (cm.card3.territory.owner == selfID) reinforcements += 2;

      gameState.playerStates[(int)selfID].Cards.Remove(cm.card1);
      gameState.playerStates[(int)selfID].Cards.Remove(cm.card2);
      gameState.playerStates[(int)selfID].Cards.Remove(cm.card3);

      return reinforcements;
    }
  }
}
